<?php

class Application_Service_Persistence_Store
{
    private $_db;
    private $_config;

    // ========//
    //   INIT  //
    // ========//
    
    public function __construct($db, $config)
    {
        $this->_db = $db;
        $this->loadConfig($config); 
    }
    
    
    private function loadConfig($filename)
    {
        $this->_config = array();        
        $xmlConfiguration = simplexml_load_file($filename);
        
        foreach($xmlConfiguration as $name => $object)
        {
            $attributes = $object->table;
            
            $config = array(
                "table"     => (string) $object['table'],
                "class"     => (string) $object['class'],
                "primary"   => (string) $object['primary'],
                "fields"    => array()
            );
            
            foreach ($object->field as $field)
            {
                $config['fields'][] = array(
                    "name"      => (string) $field['name'],
                    "column"    => (string) $field['column'],
                    "link"      => (string) $field['linkedTo'] ? array(
                                "table"     => (string) $field['linkedTo']
                                   ) : null
                );
            }
            
            $this->_config[$name] = $config;
        }
    }

    // ========//
    //   LOAD  //
    // ========//
    
    public function get($type, $id = null)
    {    
        $config = $this->_config[$type];
        $query = $this->_db->select()
                    ->from($config['table']);

        if (is_array($id))
        {
            $query->where("{$config['primary']} IN ?", $id);
        }
        elseif ($id)
        {
            $query->where("{$config['primary']} = ?", $id);
        }
        
        $result = $this->getFromQuery($config, $query);
        
        if ($id && !is_array($id))
        {
            return count($result) == 1 ? $result[0] : null;
        }
        else
        {
            return $result;
        }
    }

    public function getLinked($type, $linkedField, $queryObject)
    {
        $queryObjectTypeConfig = $this->getConfigForClass(get_class($queryObject));
        $pkOfQueryObject = $queryObject->{$queryObjectTypeConfig['primary']};

        $parentConfig = $this->_config[$type];
        $parentField = null;
        foreach($parentConfig['fields'] as $field)
        {
            if ($field['name'] == $linkedField)
            {
                $parentField = $field;
                break;
            } 
        }

        if($parentField == null)
        {
            throw new Exception("$linkedField was not found in $type object.");
        }

        $query = $this->_db->select()
                            ->from($parentConfig['table'])
                            ->where("{$parentField['column']} = ?", $pkOfQueryObject);
        return  $this->getFromQuery($parentConfig, $query);
    }

    private function getFromQuery($config, $query)
    {
        $objects = array();

        $resultSet = $this->_db->query($query)->fetchAll();
        foreach($resultSet as $record)
        {
            $instance = new $config['class'];
                  
            foreach($config['fields'] as $field)
            {            
                if($field['link'])
                {
                    $instance->{$field['name']} = $this->get($field['link']['table'], $record[$field['column']]);
                } 
                else
                {
                    $instance->{$field['name']} = $record[$field['column']];
                }                
            }

            $objects[] = $instance;
        }

        return $objects;
    }

    // ========//
    //   SAVE  //
    // ========//
    
    public function save($object)
    {
        // Find the type
        $config = $this->getConfigForClass(get_class($object));
        
        if ($config == null)
        {
            throw new Exception("Cannot find configuration for " . get_class($object));
        }
        
        $fieldsToUpdate = array();
        
        foreach($config['fields'] as $field)
        {
            if ($field['column'] == $config['primary'])
            {
                continue;
            }

            if($field['link'])
            {
                $pk = $this->_config[$field['link']['table']]['primary'];                
                $fieldsToUpdate[$field['column']] = $object->{$field['name']}->{$pk};
            }
            else
            {            
                $fieldsToUpdate[$field['column']] = $object->{$field['name']};
            }
        }
  
        if ($object->{$config['primary']} != null)
        {  
            $updatedRows = $this->_db->update(
                    $config['table'], 
                    $fieldsToUpdate, 
                    $this->_db->quoteInto("{$config['primary']} = ?", $object->{$config['primary']}));
        }
        else
        {
            $updatedRows = $this->_db->insert(
                    $config['table'],
                    $fieldsToUpdate);

            $object->{$config['primary']} = $this->_db->lastInsertId();
        }
                            
        return $updatedRows == 1;
    }
    
    private function getConfigForClass($class)
    {
        foreach($this->_config as $config)
        {
            if ($config['class'] == $class)
            {
                return $config;
            }
        }
        
        return null;
    }

    // ========//
    //   DROP  //
    // ========//
    
    public function delete($object)
    {
        // Find the type
        $config = $this->getConfigForClass(get_class($object));
        
        if ($config == null)
        {
            throw new Exception("Cannot find configuration for " . get_class($object));
        }
        
        $deletedRows = $this->_db->delete(
                    $config['table'], 
                    $this->_db->quoteInto("{$config['primary']} = ?", $object->{$config['primary']}));                    
                    
        return $deletedRows == 1;
    }
}

